package com.atguigu.gmall.list.service.Impl;
import com.atguigu.gmall.model.vo.SearchAttrListVo;
import com.atguigu.gmall.model.vo.SearchOrderMapVo;
import com.atguigu.gmall.model.vo.SearchTrademarkVo;
import com.google.common.collect.Lists;

import com.atguigu.gmall.list.dao.GoodsDao;
import com.atguigu.gmall.list.service.GoodsService;
import com.atguigu.gmall.model.list.Goods;
import com.atguigu.gmall.model.list.SearchParam;
import com.atguigu.gmall.model.vo.GoodsSearchResultVo;
import org.apache.lucene.search.join.ScoreMode;
import org.elasticsearch.index.query.BoolQueryBuilder;
import org.elasticsearch.index.query.NestedQueryBuilder;
import org.elasticsearch.index.query.QueryBuilders;
import org.elasticsearch.search.aggregations.AbstractAggregationBuilder;
import org.elasticsearch.search.aggregations.Aggregation;
import org.elasticsearch.search.aggregations.AggregationBuilders;
import org.elasticsearch.search.aggregations.Aggregations;
import org.elasticsearch.search.aggregations.bucket.nested.NestedAggregationBuilder;
import org.elasticsearch.search.aggregations.bucket.nested.ParsedNested;
import org.elasticsearch.search.aggregations.bucket.terms.ParsedLongTerms;
import org.elasticsearch.search.aggregations.bucket.terms.ParsedStringTerms;
import org.elasticsearch.search.aggregations.bucket.terms.Terms;
import org.elasticsearch.search.aggregations.bucket.terms.TermsAggregationBuilder;
import org.elasticsearch.search.fetch.subphase.highlight.HighlightBuilder;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.domain.PageRequest;
import org.springframework.data.domain.Pageable;
import org.springframework.data.domain.Sort;
import org.springframework.data.elasticsearch.core.ElasticsearchRestTemplate;
import org.springframework.data.elasticsearch.core.SearchHit;
import org.springframework.data.elasticsearch.core.SearchHits;
import org.springframework.data.elasticsearch.core.mapping.IndexCoordinates;
import org.springframework.data.elasticsearch.core.query.HighlightQuery;
import org.springframework.data.elasticsearch.core.query.NativeSearchQuery;
import org.springframework.data.elasticsearch.core.query.Query;
import org.springframework.stereotype.Service;
import org.springframework.util.StringUtils;

import java.util.ArrayList;
import java.util.List;

@Service
public class GoodsServiceImpl implements GoodsService {
    @Autowired
    GoodsDao goodsDao;
    @Autowired
    ElasticsearchRestTemplate elasticsearchRestTemplate;

    @Override
    public void saveGoods(Goods goods) {
        goodsDao.save(goods);
    }

    @Override
    public void deleteGoods(Long skuId) {
        goodsDao.deleteById(skuId);
    }

    /**
     * 检索
     * @param param
     * @return
     */
    @Override
    public GoodsSearchResultVo searchGoods(SearchParam param) {
        //1.根据前端传递来的参数,构造复杂的检索条件
        Query query=buildQueryBySearchParam(param);

        //2.检索
        SearchHits<Goods> hits = elasticsearchRestTemplate.search(query, Goods.class, IndexCoordinates.of("goods"));
        //3.数据提取
        GoodsSearchResultVo resultVo = buildResponse(hits, param);

        return resultVo;

    }
    //根据前端传来的复杂检索条件,构造自己的检索query
    private Query buildQueryBySearchParam(SearchParam param) {
        //1.构建一个bool query
        BoolQueryBuilder boolQuery = QueryBuilders.boolQuery();
        //2.bool-must
        if (param.getCategory3Id()!=null){
            boolQuery.must(QueryBuilders.termQuery("category3Id", param.getCategory3Id()));

        }
        if (param.getCategory2Id()!=null){
            boolQuery.must(QueryBuilders.termQuery("category2Id", param.getCategory2Id()));

        }
        if (param.getCategory1Id()!=null){
            boolQuery.must(QueryBuilders.termQuery("category1Id", param.getCategory1Id()));

        }
        //按照品牌来查 trademark=2:苹果
        if(!StringUtils.isEmpty(param.getTrademark())){
            String[] split = param.getTrademark().split(":");
            boolQuery.must(QueryBuilders.termQuery("tmId", split[0]));
        }
        //按照商品名来查,模糊匹配
        if (!StringUtils.isEmpty(param.getKeyword())){
            boolQuery.must(QueryBuilders.matchQuery("title", param.getKeyword()));
        }
        //按照平台属性进行检索 props=24:128G:机身内存
        if(param.getProps()!=null&&param.getProps().length>0){
            for (String prop : param.getProps()) {
                //拼装一个nested的属性查询24:128G:机身内存
                String[] split = prop.split(":");
                BoolQueryBuilder nestQueryBuilder = QueryBuilders.boolQuery();
                nestQueryBuilder.must(QueryBuilders.termQuery("attrs.attrId", split[0]));
                nestQueryBuilder.must(QueryBuilders.termQuery("attrs.attrValue", split[1]));
                NestedQueryBuilder nestQuery = QueryBuilders.nestedQuery("attrs", nestQueryBuilder, ScoreMode.None);
                boolQuery.must(nestQueryBuilder);

            }
        }
        //====构造查询结束=====


        //代表完整的检索条件
        NativeSearchQuery query = new NativeSearchQuery(boolQuery);

        //===构造排序开始====
        if(!StringUtils.isEmpty(param.getOrder())){
            String[] split = param.getOrder().split(":");
            String sortField = "";
            switch (split[0]){
                case "1": sortField = "hotScore" ;break;
                case "2": sortField = "price" ; break;
                default:sortField= "hotScore";
            }
            Sort by = Sort.by(Sort.Direction.fromString(split[1]), sortField);
            query.addSort(by);
        }



        //=====构造分页开始====

        Pageable pageable=PageRequest.of(param.getPageNo()-1, param.getPageSize());

        query.setPageable(pageable);

        //=====添加高亮=====
        if(!StringUtils.isEmpty(param.getKeyword())){
            HighlightBuilder highlightBuilder = new HighlightBuilder();
            highlightBuilder.field("title").preTags("<span style='color:red>").postTags("</span>");
            HighlightQuery highlightQuery = new HighlightQuery(highlightBuilder);
            query.setHighlightQuery(highlightQuery);
        }

        //=======添加品牌聚合条件========

        TermsAggregationBuilder tmAgg = AggregationBuilders.terms("tmAgg").field("tmId").size(100);
        //子聚合品牌名
        tmAgg.subAggregation(AggregationBuilders.terms("tmNameAgg").field("tmName").size(1));
        //子聚合品牌Logo
        tmAgg.subAggregation(AggregationBuilders.terms("tmLogoUrlAgg").field("tmLogoUrl").size(1));

        query.addAggregation(tmAgg);

        //==========添加平台属性聚合条件(嵌入式)========
        NestedAggregationBuilder attrsAgg = AggregationBuilders.nested("attrsAgg", "attrs");
        //平台属性的子聚合平台id聚合
        TermsAggregationBuilder attrIdAgg = AggregationBuilders.terms("attrIdAgg").field("attrs.attrId").size(100);
        attrsAgg.subAggregation(attrIdAgg);
        //聚合平台id聚合的子聚合(平台属性名聚合)
        TermsAggregationBuilder attrNameAgg = AggregationBuilders.terms("attrNameAgg").field("attrs.attrName").size(1);
        //聚合平台id聚合的子聚合(平台属性值聚合)
        TermsAggregationBuilder attrValueAgg = AggregationBuilders.terms("attrValueAgg").field("attrs.attrValue").size(10);
        attrIdAgg.subAggregation(attrNameAgg);
        attrIdAgg.subAggregation(attrValueAgg);
        query.addAggregation(attrsAgg);


        return query;

    }

    //根据检索结果构建响应数据
    private GoodsSearchResultVo buildResponse(SearchHits<Goods> hits, SearchParam param) {
        GoodsSearchResultVo resultVo = new GoodsSearchResultVo();
        resultVo.setSearchParam(param);

        String trademark = param.getTrademark();
        if (!StringUtils.isEmpty(trademark)){
            //品牌面包屑
            //品牌: 华为
            String[] split = trademark.split(":");
            resultVo.setTrademarkParam("品牌:"+split[1]);
        }


        //url参数
        String urlParam=buildUrlParam(param);
        resultVo.setUrlParam(urlParam);




        resultVo.setPropsParamList(Lists.newArrayList());
        //检索区:品牌列表信息:根据查询的响应JSON数据:得到品牌列表信息
        List<SearchTrademarkVo> trademarkList = analyseTrademarkList(hits);
        resultVo.setSearchTrademarkList(trademarkList);

        //检索区:平台属性列表: 根据查询的响应JSON数据:得到平台属性列表信息
        List<SearchAttrListVo> attrListVos = analyseAttrList(hits);

        resultVo.setAttrListList(attrListVos);


        resultVo.setOrderMap(new SearchOrderMapVo());

        List<Goods> goods =Lists.newArrayList();
        for (SearchHit<Goods> hit : hits) {
            //获取命中记录的商品真正数据
            Goods content = hit.getContent(); //hits中每个记录的_source部分
            goods.add(content);
        }

        resultVo.setGoodsList(goods);

        //页码

        resultVo.setPageNo(param.getPageNo());
        //总页数,hits数是每页大小的整数倍吗谁,不是就加一
        Long l=hits.getTotalHits()%param.getPageSize()==0?hits.getTotalHits()/param.getPageSize():hits.getTotalHits()/param.getPageSize()+1;
        resultVo.setTotalPages(l.intValue());
        //todo 翻页溢出总是跳到最后一页




        return resultVo;
    }

    /**
     * 根据前端发送请求时的请求参数,拼装出查询字符串
     * 忽略掉order字段,前端会自己拼装
     * @param param
     * @return
     */
    private String buildUrlParam(SearchParam param) {

        StringBuilder sb = new StringBuilder("list.html?");
        if(param.getCategory1Id()!=null){
            sb.append("category1Id="+param.getCategory1Id()+"&");
        }

        if (param.getCategory2Id()!=null){
            sb.append("category2Id="+param.getCategory2Id()+"&");
        }
        if (param.getCategory3Id()!=null){
            sb.append("category3Id="+param.getCategory3Id()+"&");
        }
        if (!StringUtils.isEmpty(param.getTrademark())){
            sb.append("trademark="+param.getTrademark()+"&");

        }
        if (!StringUtils.isEmpty(param.getKeyword())){
            sb.append("keyword="+param.getKeyword()+"&");
        }
        if (param.getProps()!=null&&param.getProps().length>0){
            for (String prop : param.getProps()) {
                sb.append("props="+prop+"&");
            }
        }
        sb.append("pageNo="+param.getPageNo()+"&");
        sb.append("pageSize="+param.getPageSize()+"&");



        return sb.toString();
    }

    /**
     * 检索区:平台属性列表: 根据查询的响应JSON数据:得到平台属性列表信息
     * @param hits
     * @return
     */
    private List<SearchAttrListVo> analyseAttrList(SearchHits<Goods> hits) {
        ArrayList<SearchAttrListVo> result = new ArrayList<>();
        Aggregations aggregations = hits.getAggregations();
        //attrsAgg聚合
        ParsedNested attrsAgg = aggregations.get("attrsAgg");
        //attrsAgg聚合的子聚合attrIdAgg
        ParsedLongTerms attrIdAgg = attrsAgg.getAggregations().get("attrIdAgg");
        //遍历子聚合attrIdAgg得到id
        for (Terms.Bucket attrIdAggBucket : attrIdAgg.getBuckets()) {
            SearchAttrListVo attrListVo = new SearchAttrListVo();
            //遍历得到key,key就是id
            Number keyAsNumber = attrIdAggBucket.getKeyAsNumber();
            attrListVo.setAttrId(keyAsNumber.longValue());
            //得到子聚合attrNameAgg
            ParsedStringTerms   attrNameAgg = attrIdAggBucket.getAggregations().get("attrNameAgg");
            for (Terms.Bucket nameAggBucket : attrNameAgg.getBuckets()) {
                String keyAsString = nameAggBucket.getKeyAsString();
                attrListVo.setAttrName(keyAsString);
            }
            ParsedStringTerms attrValueAgg = attrIdAggBucket.getAggregations().get("attrValueAgg");

            //得到子聚合attrValueAgg
            ArrayList<String> list = new ArrayList<>();
            for (Terms.Bucket attrValueAggBucket : attrValueAgg.getBuckets()) {
                String keyAsString = attrValueAggBucket.getKeyAsString();
                list.add(keyAsString);

            }
            attrListVo.setAttrValueList(list);

            result.add(attrListVo);
        }
        //返回属性集合
        return result;
    }

    /**
     * 根据检索到的整个结果,分析出品牌列表信息
     * @param hits
     * @return
     */
    private List<SearchTrademarkVo> analyseTrademarkList(SearchHits<Goods> hits) {
        ArrayList<SearchTrademarkVo> result = new ArrayList<>();
        Aggregations aggregations = hits.getAggregations();
        ParsedLongTerms tmAgg = aggregations.get("tmAgg");
        for (Terms.Bucket bucket : tmAgg.getBuckets()) {
            SearchTrademarkVo vo = new SearchTrademarkVo();
            //tmAgg得到tmId
            Long key = (Long) bucket.getKey();
            Aggregations bucketAggregations = bucket.getAggregations();

            //tmNameAgg聚合中得到名称
            ParsedStringTerms tmNameAgg = bucketAggregations.get("tmNameAgg");
            for (Terms.Bucket tmNameAggBucket : tmNameAgg.getBuckets()) {
                String name = tmNameAggBucket.getKeyAsString();
                vo.setTmName(name);
            }
            //tmLogoUrlAgg聚合中得到tmLogoUrlAgg
            ParsedStringTerms tmLogoUrlAgg = bucketAggregations.get("tmLogoUrlAgg");
            for (Terms.Bucket tmLogoUrlAggBucket : tmLogoUrlAgg.getBuckets()) {
                String tmLogoUrl =tmLogoUrlAggBucket.getKeyAsString();
                vo.setTmLogoUrl(tmLogoUrl);
            }
            vo.setTmId(key);

            result.add(vo);
        }

        return result;

    }
}

